package com.anji.plus.gaea.job.trigger.scheduler;


import com.anji.plus.gaea.job.core.constant.ExecutorBlockStrategyEnum;
import com.anji.plus.gaea.job.core.constant.TriggerTypeEnum;
import com.anji.plus.gaea.job.core.dto.ReturnT;
import com.anji.plus.gaea.job.core.param.TriggerParam;
import com.anji.plus.gaea.job.core.util.ThrowableUtil;
import com.anji.plus.gaea.job.executor.util.IpUtil;
import com.anji.plus.gaea.job.trigger.dao.DaoService;
import com.anji.plus.gaea.job.trigger.dao.entity.MetaJobInfo;
import com.anji.plus.gaea.job.trigger.dao.entity.MetaJobLog;
import com.anji.plus.gaea.job.trigger.route.ExecutorRouteStrategyEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.List;

/**
 * xxl-job trigger
 * Created by xuxueli on 17/7/13.
 *
 * Borrowed from xxljob v2.4.0
 */
public class XxlJobTrigger {
    private static Logger logger = LoggerFactory.getLogger(XxlJobTrigger.class);

    /**
     * trigger job
     *
     * @param jobId
     * @param triggerType
     * @param failRetryCount
     * 			>=0: use this param
     * 			<0: use param from job info config
     * @param executorShardingParam
     * @param executorParam
     *          null: use job param
     *          not null: cover job param
     * @param addressList
     *          null: use executor addressList
     *          not null: cover
     */
    public static void trigger(long jobId,
                               TriggerTypeEnum triggerType,
                               int failRetryCount,
                               String executorShardingParam,
                               String executorParam,
                               String addressList) {

        // load data
        MetaJobInfo jobInfo = DaoService.getInstance().queryMetaJobInfoById(jobId);
        if (jobInfo == null) {
            logger.warn(">>>>>>>>>>>> trigger fail, jobId invalid，jobId={}", jobId);
            return;
        }
        if (executorParam != null) {
            jobInfo.setJobParam(executorParam);
        }
        int finalFailRetryCount = failRetryCount>=0?failRetryCount:jobInfo.getJobFailRetryCount();

        List<String> executorUpAddresses = DaoService.getInstance().queryMetaJobExecutorUpAddress(jobInfo.getExecutorCode());
        processTrigger(executorUpAddresses, jobInfo, finalFailRetryCount, triggerType);
        /*
        XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(jobInfo.getJobGroup());
        if (addressList!=null && addressList.trim().length()>0) {
            group.setAddressType(1);
            group.setAddressList(addressList.trim());
        }

        // 去掉分片广播功能
        int[] shardingParam = null;
        if (executorShardingParam!=null){
            String[] shardingArr = executorShardingParam.split("/");
            if (shardingArr.length==2 && isNumeric(shardingArr[0]) && isNumeric(shardingArr[1])) {
                shardingParam = new int[2];
                shardingParam[0] = Integer.valueOf(shardingArr[0]);
                shardingParam[1] = Integer.valueOf(shardingArr[1]);
            }
        }
        if (ExecutorRouteStrategyEnum.SHARDING_BROADCAST==ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null)
                && executorUpAddresses!=null && !executorUpAddresses.isEmpty()
                && shardingParam==null) {
            for (int i = 0; i < executorUpAddresses.size(); i++) {
                processTrigger(executorUpAddresses, jobInfo, finalFailRetryCount, triggerType, i, executorUpAddresses.size());
            }
        } else {
            if (shardingParam == null) {
                shardingParam = new int[]{0, 1};
            }
            processTrigger(executorUpAddresses, jobInfo, finalFailRetryCount, triggerType, shardingParam[0], shardingParam[1]);
        }
        */

    }

    private static boolean isNumeric(String str){
        try {
            int result = Integer.valueOf(str);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    /**
     * @param executorUpAddresses       job对应的执行器在线地址列表
     * @param jobInfo
     * @param finalFailRetryCount
     * @param triggerType
     */
    private static void processTrigger(List<String> executorUpAddresses, MetaJobInfo jobInfo, int finalFailRetryCount, TriggerTypeEnum triggerType){
        // param
        ExecutorBlockStrategyEnum blockStrategy = ExecutorBlockStrategyEnum.match(jobInfo.getJobBlockStrategy(), ExecutorBlockStrategyEnum.SERIAL_EXECUTION);  // block strategy
        ExecutorRouteStrategyEnum executorRouteStrategyEnum = ExecutorRouteStrategyEnum.match(jobInfo.getJobRouteStrategy(), null);    // route strategy
        //String shardingParam = (ExecutorRouteStrategyEnum.SHARDING_BROADCAST==executorRouteStrategyEnum)?String.valueOf(index).concat("/").concat(String.valueOf(total)):null;

        // 1、save log-id
        Date triggerTime = new Date();
        long logId = DaoService.getInstance().saveMetaJobLog(jobInfo.getId(), jobInfo.getTenantCode(), jobInfo.getOrgCode(), jobInfo.getExecutorCode(), triggerTime);
        logger.debug(">>>>>>>>>>> xxl-job trigger start, jobId:{}", logId);

        // 2、init trigger-param
        TriggerParam triggerParam = new TriggerParam();
        triggerParam.setJobId(jobInfo.getId());
        triggerParam.setTenantCode(jobInfo.getTenantCode());
        triggerParam.setOrgCode(jobInfo.getOrgCode());
        triggerParam.setExecutorCode(jobInfo.getExecutorCode());
        triggerParam.setJobType(jobInfo.getJobType());
        triggerParam.setJobSource(jobInfo.getJobSource());
        triggerParam.setJobSourceUpdateTime(jobInfo.getJobSourceUpdateTime().getTime());
        triggerParam.setJobHandler(jobInfo.getJobHandler());
        triggerParam.setJobParams(jobInfo.getJobMergedParam()); // 租户、机构等合并到jobParams中
        triggerParam.setJobBatch(jobInfo.getJobBatch());
        triggerParam.setJobBlockStrategy(jobInfo.getJobBlockStrategy());
        triggerParam.setJobTimeout(jobInfo.getJobTimeout());
        triggerParam.setLogId(logId);
        triggerParam.setLogDateTime(triggerTime.getTime());
//        triggerParam.setGlueType(jobInfo.getGlueType());
//        triggerParam.setGlueSource(jobInfo.getGlueSource());
//        triggerParam.setGlueUpdatetime(jobInfo.getGlueUpdatetime().getTime());
//        triggerParam.setBroadcastIndex(index);
//        triggerParam.setBroadcastTotal(total);

        // 3、init address
        String address = null;
        ReturnT<String> routeAddressResult = null;
        if (executorUpAddresses!=null && ! executorUpAddresses.isEmpty()) {
//            if (ExecutorRouteStrategyEnum.SHARDING_BROADCAST == executorRouteStrategyEnum) {
//                if (index < executorUpAddresses.size()) {
//                    address = executorUpAddresses.get(index);
//                } else {
//                    address = executorUpAddresses.get(0);
//                }
//            } else {
//
//            }
            routeAddressResult = executorRouteStrategyEnum.getRouter().route(triggerParam, executorUpAddresses);
            if (routeAddressResult.getCode() == ReturnT.SUCCESS_CODE) {
                address = routeAddressResult.getContent();
            }
        } else {
            routeAddressResult = new ReturnT<String>(ReturnT.FAIL_CODE, "调度失败：执行器地址为空");
        }

        // 4、trigger remote executor
        ReturnT<String> triggerResult = null;
        if (address != null) {
            triggerResult = runExecutor(triggerParam, address);
        } else {
            triggerResult = new ReturnT<String>(ReturnT.FAIL_CODE, "根据任务的路由策略找不到有效的执行器地址");
        }

        // 5、collection trigger info
        StringBuffer triggerMsgSb = new StringBuffer();
        triggerMsgSb.append("任务触发类型：").append(triggerType.getTitle());
        triggerMsgSb.append("<br>调度机器：").append(IpUtil.getIp());
        triggerMsgSb.append("<br>执行器-注册方式：自动注册");
        triggerMsgSb.append("<br>执行器-地址列表：").append(executorUpAddresses);
        triggerMsgSb.append("<br>路由策略：").append(executorRouteStrategyEnum.getTitle());
//        if (shardingParam != null) {
//            triggerMsgSb.append("("+shardingParam+")");
//        }
        triggerMsgSb.append("<br>阻塞处理策略：").append(blockStrategy.getTitle());
        triggerMsgSb.append("<br>任务超时时间：").append(jobInfo.getJobTimeout());
        triggerMsgSb.append("<br>失败重试次数：").append(finalFailRetryCount);

        triggerMsgSb.append("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>")
                .append((routeAddressResult!=null&&routeAddressResult.getMsg()!=null)?routeAddressResult.getMsg()+"<br><br>":"").append(triggerResult.getMsg()!=null?triggerResult.getMsg():"");

        // 6、save log trigger-info
        MetaJobLog metaJobLog = new MetaJobLog();
        metaJobLog.setId(logId);
        metaJobLog.setExecutorId(null);
        metaJobLog.setExecutorAddress(address);
        metaJobLog.setJobHandler(jobInfo.getJobHandler());
        metaJobLog.setJobParam(jobInfo.getJobParam());
        metaJobLog.setJobFailRetryCount(finalFailRetryCount);
        metaJobLog.setTriggerStatus(triggerResult.getCode());
        metaJobLog.setTriggerMsg(triggerMsgSb.toString());
        metaJobLog.setExecutorStatus(0);
        DaoService.getInstance().updateMetaJobLogById(metaJobLog);
        logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", logId);
    }

    /**
     * run executor
     * @param triggerParam
     * @param address
     * @return
     */
    public static ReturnT<String> runExecutor(TriggerParam triggerParam, String address){
        ReturnT<String> runResult = null;
        try {
            ExecutorClient executorClient = XxlJobScheduler.getExecutorClient(address);
            runResult = executorClient.run(triggerParam);
        } catch (Exception e) {
            logger.error(">>>>>>>>>>> xxl-job trigger error, please check if the executor[{}] is running.", address, e);
            runResult = new ReturnT<String>(ReturnT.FAIL_CODE, ThrowableUtil.toString(e));
        }

        StringBuffer runResultSB = new StringBuffer("触发调度");
        runResultSB.append("<br>address：").append(address);
        runResultSB.append("<br>code：").append(runResult.getCode());
        runResultSB.append("<br>msg：").append(runResult.getMsg());

        runResult.setMsg(runResultSB.toString());
        return runResult;
    }

}
